<?php
define('Smgp_CONNECT', 0x00000001); // 请求连接
define('Smgp_TERMINATE', 0x00000006); // 终止连接
define('Smgp_TERMINATE_RESP', 0x80000006); // 终止连接应答
define('Smgp_SUBMIT', 0x00000002); // 提交短信
define('Smgp_DELIVER', 0x00000003); // 短信下发
define('Smgp_DELIVER_RESP', 0x80000003); // 下发短信应答
define('Smgp_ACTIVE_TEST', 0x00000004); // 链路检测
define('Smgp_ACTIVE_TEST_RESP', 0x80000004); // 链路检测应答

class SmgpServer
{
    protected $clients;
    protected $serv;
    protected $sequence_id = 1;
    protected $redis_client;
    protected $sp_code =70094;
    protected $_message_sequence =1;

    function run()
    {
        $this->serv = new swoole_server("127.0.0.1", 9504);
        $this->serv->set(array(
            'timeout' => 1, //select and epoll_wait timeout.
            'daemonize' => 1,  //以守护进程执行
            'reactor_num' => 1, //线程数
            'worker_num' => 1, //进程数
            'backlog' => 128, //Listen队列长度
            'max_conn' => 10000,
            'max_request' => 3000,
            'task_max_request'=>3000,
            'dispatch_mode' => 1,
        ));
        $this->serv->on('workerstart', array($this, 'onStart'));
        $this->serv->on('Receive', array($this, 'onReceive'));
        $this->serv->on('close', array($this, 'onReceive'));
        $this->serv->start();
    }
    /**
     * 获得redis连接
     * @return bool|null|Redis
     */
    private function redis_conn()
    {
        if (!$this->redis_client) {
            try {
                $this->redis_client = new Redis();
                if (!$this->redis_client->pconnect('127.0.0.1', '6379')) {
                    return false;
                }
                if (!$this->redis_client->auth('123456')) {
                    return false;
                }

            } catch (\Exception $e) {
                return false;
            }
        }
        return $this->redis_client;
    }

    /*
     * BCD解码
     */
    private function bcd_to_str($pdu){
        $str = '';
        $len = strlen($pdu);
        for($i = 0; $i<$len; $i++){
            $format = "Cnum";
            $data = unpack($format, substr($pdu, $i));
            $str .= ($data['num'] & 0xf0)>>4;
            $str.= $data['num']& 0x0f;
        }
        return $str;
    }

    private function Smgp_CONNECT_RESP($pdu){
        $format = "NStatus/a16Auth/CVersion";
        $data = unpack($format, $pdu);
        $status = intval($data['Status']);
        if($status !== 0){
            return false;
        }
        $client = $this->clients['sys']['socket'];
        $header = pack('NNN',12,Smgp_ACTIVE_TEST,$this->sequence_id);
        $client->send($header);
        $test = $this->sequence_id;
        $this->serv->tick(120000, function()use ($client, $test){
            $header = pack('NNN',12,Smgp_ACTIVE_TEST,$test);
            $client->send($header);
        });
    }

    private function Smgp_ACTIVE_TEST_RESP(){
        $redis = $this->redis_conn();
        $key = 'active_smgp_9505';
        $redis->set($key, date('Y-m-d H:i:s'), 150);
    }

    private function Smgp_SUBMIT_RESP($pdu, $send_list_id){
        $pdu_msg_id = substr($pdu, 0, 10);
        $msg_id = $this->bcd_to_str($pdu_msg_id);
        $format = "a10/CResult";
        $data = unpack($format, $pdu);
        $status = $data['Result'];
        $redis = $this->redis_conn();

        // code_monitor
        $key = 'code_monitor_'.$send_list_id;
        $redis->set($key, 3, 3600*24);

        if($status === 0){
            $key = 'yanzhengma_'.$this->sp_code.'_'.$msg_id;
            $redis->set($key, $send_list_id, 3600*24);
        }else{
            $key = 'yanzhengma_send_result_list';
            $array['send_list_id'] = $send_list_id;
            $array['status'] = 0;
            $array['msg_id'] = $msg_id;
            $array['code'] = 'error';
            $array['receive_time'] = date('Y-m-d H:i:s');
            $json = json_encode($array);
            $redis->rpush($key,$json);
        }
    }

    private function SEND_Smgp_DELIVER_RESP($sequence_id, $msg_id_pdu, $result){
        $Status = unpack('N', $result);
        $header = pack('NNN',26,Smgp_DELIVER_RESP,$sequence_id);
        $client = $this->clients['sys']['socket'];
        $data = $header.$msg_id_pdu.$Status;
        $client->send($data);
    }

    private function Smgp_DELIVER_RESP($data){
        $pdu = substr($data, 12);
        $content_length = strlen($pdu) - 77;
        $format = "a10MsgID/CIsReport/CMsgFormat/a14RecvTime/a21SrcTermID/a21DestTermID/CMsgLength/a{$content_length}MsgContent/a8Reserve";
        $unpack_data = unpack($format, $pdu);
        $msg_content = $unpack_data['MsgContent'];
        $status_arr = explode(' ', $msg_content);
        $result = [];
        foreach ($status_arr as $value) {
            $temp = explode(':', $value);
            if(count($temp) == 2){
                $result[$temp[0]] = $temp[1];
            }
        }
        $pdu_msg_id = substr($pdu, 0, 10);
        $msg_id = $this->bcd_to_str($result['id']);
        $stat = $result['stat'];
        $status = 0;
        if($stat == 'DELIVRD'){
            $status = 1;
        }
        $redis = $this->redis_conn();
        $key1 = 'yanzhengma_'.$this->sp_code.'_'.$msg_id;
        $send_list_id = $redis->get($key1);

        // code_monitor
        $key = 'code_monitor_'.$send_list_id;
        $redis->set($key, 4, 3600*24);

        if($send_list_id){
            $array['send_list_id'] = $send_list_id;
            $array['status'] = $status;
            $array['msg_id'] = $msg_id;
            $array['code'] = $stat;
            $array['receive_time'] = date('Y-m-d H:i:s');
            $json = json_encode($array);
            $key = 'yanzhengma_send_result_list';
            $res = $redis->rpush($key,$json);
        }
        extract(unpack("Nsequence_id", substr($data, 8, 4)));
        $this->SEND_Smgp_DELIVER_RESP($sequence_id, $pdu_msg_id, 0);
    }

    private function split_message_unicode($text)
    {
        $max_len = 134;
        $res = array();
        if (mb_strlen($text) <= 140) {
            $res[] = $text;
            return $res;
        }
        $pos = 0;
        $msg_sequence = $this->_message_sequence++;
        $num_messages = ceil(mb_strlen($text) / $max_len);
        $part_no = 1;
        while ($pos < mb_strlen($text)) {
            $ttext = mb_substr($text, $pos, $max_len);
            $pos += mb_strlen($ttext);
            $udh = pack("cccccc", 5, 0, 3, $msg_sequence, $num_messages, $part_no);
            $part_no++;
            $res[] = $udh . $ttext;
        }
        return $res;
    }

    /*
     * 连接
     */
    private function clientStart(){
        $socket = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
        $this->clients['sys'] = array(
            'socket' => $socket,
        );

        $socket->on("connect", function(swoole_client $cli) {
            $user = 70094;
            $password = 'DASerthgf';
            $timestamp = date('mdHis');
            $login_mode = 0x20;
            $version = 0x30;
            $AuthenticatorSource_ori = $user . pack('a7', '') . $password . $timestamp;
            $AuthenticatorSource = md5($AuthenticatorSource_ori, true);
            $format = "a8a16CNC";
            //消息体
            $body = pack($format, $user, $AuthenticatorSource,$login_mode, $timestamp, $version);
            //消息长度
            $body_len =strlen($body)+12;
            //消息头
            $header = pack('NNN',$body_len,Smgp_CONNECT,$this->sequence_id);
            $this->sequence_id = $this->sequence_id + 1;
            $data = $header.$body;
            $cli->send($data);
        });

        $socket->on("receive", function(swoole_client $cli, $data){
            if($data){
                extract(unpack("Nlength/Ncommand_id/Nsequence_number", $data));
                $command_id &= 0x0fffffff;
                $pdu = substr($data, 12);
                switch ($command_id) {
                    case Smgp_CONNECT:
                        $data = $this->Smgp_CONNECT_RESP($pdu);
                        break;
                    case Smgp_SUBMIT:
                       $data = $this->Smgp_SUBMIT_RESP($pdu, $sequence_number);
                       break;
                    case Smgp_DELIVER:
                       $data = $this->Smgp_DELIVER_RESP($data);
                       break;
                    case Smgp_ACTIVE_TEST:
                       $data = $this->Smgp_ACTIVE_TEST_RESP();
                       break;
                    default:
                        break;
                }
            }
        });
        $socket->on("error", function(swoole_client $cli){
            echo "error\n";
        });
        $socket->on("close", function(swoole_client $cli){
            echo "Connection close\n";
            $this->clientStart();
        });
        $socket->connect('58.253.87.82', 8890);
    }

    function onStart($serv)
    {
        $this->serv = $serv;
        echo "Server: start.Swoole version is [" . SWOOLE_VERSION . "]\n";
        $this->clientStart();
    }

    function onConnect($serv, $fd, $from_id)
    {
    }

    function onReceive($serv, $fd, $from_id, $data)
    {
        $temp = json_decode($data, true);
        $mobile = $temp['mobile'];
        $content = $temp['content'];
        $send_list_id = $temp['send_list_id']; //发送总表的ID
        if($mobile && $content && $send_list_id){
            $redis = $this->redis_conn();
            $key = 'code_monitor_'.$send_list_id;
            $redis->set($key, 2, 3600*24);
            $unicode_text = mb_convert_encoding($content, "UCS-2BE", 'UTF-8');
            /* 中文要用UCS-2BE编码，要不收到短信是乱码 */
            $multi_texts = $this->split_message_unicode($unicode_text);
            unset($unicode_text);
            reset($multi_texts);
            list($pos, $part) = each($multi_texts);
            $msg_content = $part;
            $msg_len = strlen($msg_content);//短信长度
            $MsgType = 6;
            $NeedReport = 1;
            $Priority = 3;
            $ServiceID = 'code3';
            $FeeType = '';
            $FeeCode = '';
            $FixedFee = '';
            $MsgFormat = 8;
            $ValidTime = '';
            $AtTime = '';
            $SrcTermID = 106908970091;
            $ChargeTermID = '';
            $DestTermIDCount = 1;
            $DestTermID = $mobile;
            $MsgLength = $msg_len;
            $MsgContent = $msg_content;
            $Reserve = '';
            $format = "CCCa10a2a6a6Ca17a17a21a21Ca21Ca{$msg_len}a8";
            $body = pack($format, $MsgType, $NeedReport, $Priority, $ServiceID, $FeeType, $FeeCode, $FixedFee, $MsgFormat, $ValidTime, $AtTime, $SrcTermID, $ChargeTermID, $DestTermIDCount, $DestTermID, $MsgLength, $MsgContent, $Reserve);

            //消息长度
            $body_len =strlen($body)+12;
            //消息头
            $header = pack('NNN',$body_len,Smgp_SUBMIT,$send_list_id);
            $data = $header.$body;
            $socket = $this->clients['sys']['socket'];
            $socket->send($data);
        }
    }
}
$serv = new SmgpServer();
$serv->run();
